package com.llb666.e.shop.domain.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.llb666.e.shop.api.dto.UserRegisterDto;
import com.llb666.e.shop.api.vo.UsersVo;
import com.llb666.e.shop.domain.entity.Users;
import com.llb666.e.shop.domain.entity.bo.ShopCartBo;
import com.llb666.e.shop.domain.repository.UserRepository;
import com.llb666.e.shop.infracore.config.RedisOperator;
import com.llb666.e.shop.infracore.constants.AppConstant;
import com.llb666.e.shop.infracore.exception.AppException;
import com.llb666.e.shop.infracore.utils.CookieUtils;
import com.llb666.e.shop.infracore.utils.MD5Utils;
import com.llb666.e.shop.infracore.web.BaseResponse;
import com.llb666.e.shop.infracore.web.CommonError;
import com.llb666.e.shop.infracore.web.DataResponse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.n3r.idworker.Sid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @author 斌~
 * @date 2022/8/7 下午9:25
 */
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class UserService {

    private final UserRepository userRepository;
    private final RedisOperator redisOperator;
    private final Sid sid;

    /**
     * 判断用户名称是否存在.
     *
     * @param username 用户名
     * @return Boolean
     */
    public BaseResponse queryUsernameIsExist(String username) {
        //1.判空
        if (StringUtils.isBlank(username)) {
            throw new AppException(CommonError.RequestParamsInvalid, "用户名不能为空！");
        }
        //2.查找注册名是否重复
        Boolean isExist = userRepository.queryUsernameIsExist(username);
        if (isExist) {
            throw new AppException(CommonError.RequestParamsInvalid, "用户名已经存在！");
        }
        return BaseResponse.ok();
    }

    /**
     * 登录.
     *
     * @param user     用户
     * @param request  -
     * @param response -
     * @return 用户信息
     * @throws Exception -
     */
    public DataResponse<UsersVo> login(Users user, HttpServletRequest request,
                                       HttpServletResponse response) throws Exception {
        String username = user.getUsername();
        String password = user.getPassword();
        //1.判空
        if (StringUtils.isBlank(username) ||
                StringUtils.isBlank(password)) {
            throw new AppException(CommonError.NeedAuthentication, "用户名和密码不能为空！");
        }
        //2.查询
        Users result = userRepository.login(username, MD5Utils.getMD5Str(password));
        if (result == null) {
            return DataResponse.errorMsg("用户名或密码错误密码错误！");
        }
        //实现用户redis会话:生成用户token，存入redis会话
        String uniqueToken = UUID.randomUUID().toString().trim();
        redisOperator.set(AppConstant.REDIS_USER_TOKEN + ":" + result.getId(), uniqueToken);
        UsersVo usersVo = UsersVo.Converter.INSTANCE.do2Vo(result);
        usersVo.setUserUniqueToken(uniqueToken);
        //3.缓存
        CookieUtils.setCookie(request, response, "user", JSONObject.toJSONString(usersVo), true);
        // 同步购物车数据
        syncShopCartData(result.getId(), request, response);
        return DataResponse.ok(usersVo);
    }


    /**
     * 注册.
     *
     * @param userDto  UserRegisterDto
     * @param request  -
     * @param response -
     */
    public void register(UserRegisterDto userDto, HttpServletRequest request, HttpServletResponse response) {
        String username = userDto.getUsername();
        String password = userDto.getPassword();
        String confirmPassword = userDto.getConfirmPassword();
        //1.判空.
        if (StringUtils.isBlank(username) ||
                StringUtils.isBlank(password) ||
                StringUtils.isBlank(confirmPassword)) {
            throw new AppException(CommonError.RegisterInvalid, "用户名和密码不能为空！");
        }
        //2.两次输入的密码应该保持一致.
        if (!StringUtils.equals(password, confirmPassword)) {
            throw new AppException(CommonError.RegisterInvalid, "两次密码输入不一致！");
        }
        //3.查找注册名是否重复.
        boolean isExist = userRepository.queryUsernameIsExist(username);
        if (isExist) {
            throw new AppException(CommonError.RegisterInvalid, "用户名已经存在！");
        }
        //4.实现注册.
        String userId = sid.nextShort();
        Users user = new Users();
        try {
            user = UserRegisterDto.covert(userId, userDto);
            user.setFace(AppConstant.USER_FACE);
            userRepository.insertSelective(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        user.setPassword("");
        //实现用户redis会话:生成用户token，存入redis会话.
        UsersVo usersVo = UsersVo.Converter.INSTANCE.do2Vo(user);
        //5.缓存.
        CookieUtils.setCookie(request, response, "user", JSONObject.toJSONString(usersVo), true);
        // 同步购物车数据.
        syncShopCartData(user.getId(), request, response);
    }

    /**
     * 注册,登录成功后，同步cookie和redis中的购物车数据.
     */
    private void syncShopCartData(String userId, HttpServletRequest request, HttpServletResponse response) {
        /*
         * 1. redis中无数据，如果cookie中的购物车为空，那么这个时候不做任何处理
         *                 如果cookie中的购物车不为空，此时直接放入redis中
         * 2. redis中有数据，如果cookie中的购物车为空，那么直接把redis的购物车覆盖本地cookie
         *                 如果cookie中的购物车不为空，
         *                 如果cookie中的某个商品在redis中存在，
         *                      则以cookie为主，删除redis中的，
         *                      把cookie中的商品直接覆盖redis中（参考京东）
         * 3. 同步到redis中去了以后，覆盖本地cookie购物车的数据，保证本地购物车的数据是同步最新的
         */

        // 从redis中获取购物车
        String shopCartJsonRedis = redisOperator.get(AppConstant.SHOP_MALL_CART + ":" + userId);
        // 从cookie中获取购物车
        String shopCartStrCookie = CookieUtils.getCookieValue(request, AppConstant.SHOP_MALL_CART, true);

        if (StringUtils.isBlank(shopCartJsonRedis)) {
            // 1.redis为空，cookie不为空，直接把cookie中的数据放入redis.
            if (StringUtils.isNotBlank(shopCartStrCookie)) {
                redisOperator.set(AppConstant.SHOP_MALL_CART + ":" + userId, shopCartStrCookie);
            }
        } else {
            // 2. redis不为空，cookie不为空，合并cookie和redis中购物车的商品数据（同一商品则覆盖redis）.
            if (StringUtils.isNotBlank(shopCartStrCookie)) {
                /*
                 * 1. 已经存在的，把cookie中对应的数量，覆盖redis（参考京东）
                 * 2. 该项商品标记为待删除，统一放入一个待删除的list
                 * 3. 从cookie中清理所有的待删除list
                 * 4. 合并redis和cookie中的数据
                 * 5. 更新到redis和cookie中
                 */
                List<ShopCartBo> shopCartListRedis = JSON.parseArray(shopCartJsonRedis, ShopCartBo.class);
                List<ShopCartBo> shopCartListCookie = JSON.parseArray(shopCartStrCookie, ShopCartBo.class);
                // 定义一个待删除list
                List<ShopCartBo> pendingDeleteList = new ArrayList<>();
                for (ShopCartBo redisShopCart : shopCartListRedis) {
                    String redisSpecId = redisShopCart.getSpecId();
                    for (ShopCartBo cookieShopCart : shopCartListCookie) {
                        String cookieSpecId = cookieShopCart.getSpecId();
                        if (redisSpecId.equals(cookieSpecId)) {
                            // 覆盖购买数量，不累加，参考京东
                            redisShopCart.setBuyCounts(cookieShopCart.getBuyCounts());
                            // 把cookieShopCart放入待删除列表，用于最后的删除与合并
                            pendingDeleteList.add(cookieShopCart);
                        }
                    }
                }
                // 从现有cookie中删除对应的覆盖过的商品数据
                shopCartListCookie.removeAll(pendingDeleteList);
                // 合并两个list
                shopCartListRedis.addAll(shopCartListCookie);
                // 更新到redis和cookie
                CookieUtils.setCookie(request, response, AppConstant.SHOP_MALL_CART, JSONObject.toJSONString(shopCartListRedis), true);
                redisOperator.set(AppConstant.SHOP_MALL_CART + ":" + userId, JSONObject.toJSONString(shopCartListRedis));
            } else {
                // 3.redis不为空，cookie为空，直接把redis覆盖cookie
                CookieUtils.setCookie(request, response, AppConstant.SHOP_MALL_CART, shopCartJsonRedis, true);
            }
        }
    }


    /**
     * 登出.
     *
     * @param userId   用户id
     * @param request  -
     * @param response -
     * @return BaseResponse
     */
    public BaseResponse logout(String userId, HttpServletRequest request, HttpServletResponse response) {
        // 清除cookie.
        CookieUtils.deleteCookie(request, response, "user");
        // 用户退出登录清除用户分布式缓存.
        redisOperator.del(AppConstant.REDIS_USER_TOKEN + ":" + userId);
        // 清除购物车.
        CookieUtils.deleteCookie(request, response, AppConstant.SHOP_MALL_CART);
        return BaseResponse.ok();
    }
}
